/*
 * mkentity.c - building entity
 * cezanne@clunix.com, 2005.3
 *
 * Copyright (c) 2001-2008 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "mkentity.h"
#include "mkeparser.h"

#ifndef _WIN32
#define MODSO_EXT	"so"
#else
#define MODSO_EXT	"dll"
#endif

static int	edbd_port = 0;
ECMBOOL	install_mode = ECM_FALSE;
ECMBOOL	dryrun_mode = ECM_FALSE;
char	*inst_clxhome;

extern int mkeparse(sr_t **);
static FILE	*fp_input;

extern ECMBOOL make_spec_obj(const char *modname, sr_t *sr);
extern ECMBOOL process_binding(sr_t *sr_sobjinfos);

enum {
	NONE_MODE,
	COMPILE_MODE,
	BINDING_MODE,
} mode = NONE_MODE;

static const char	*modname;
const char		*specsrc;
const char		*specdir;
static sr_t		*sr_sobjinfos;
static ecm_uint32	sobj_num, nonstandard_sobj_num;
static char		*mke_clxdir;

void
mke_input(char *buf, int *result, int max)
{
	ecm_size_t	buflen = (ecm_size_t)max;
	if (ecm_read(ecm_fileno(fp_input), buf, &buflen))
		*result = buflen;
	else {
		*result = 0;
	}
}

static char *
check_modname(const char *c_filename, const char *ext)
{
	char	*filename, *fname, *dot;
	char	*modname = NULL;

	filename = ecm_strdup(c_filename);
	fname = (char *)ecm_basename(filename);
	specdir = (char *)ecm_dirname(ecm_strdup(filename));
	dot = ecm_strrchr(fname, '.');
	if (dot && fname != dot && strcmp(dot + 1, ext) == 0) {
		*dot = '\0';
		modname = ecm_strdup(fname);
	}
	ecm_free(filename);
	return modname;
}

static void
usage(void)
{
	fprintf(stderr,
"Usage: mkentity has two running modes.\n"
"\n"
"[compile mode]\n"
" mkentity -c <module spec>\n"
"\n"
"[binding mode]\n"
" mkentity <options> [-S <standard module name>]* [-B <spec obj>]* <module so>...\n"
" <options>:\n"
"   -i <install path>: install mode. install snapshot, smod, module so\n"
"   -n: dry run mode. No output would be generated\n"
"         \"-B <spec obj>\" may be specified multiple times. \n"
"\n");
}

#ifdef ECM_DEBUG

extern int	mke_flex_debug;
extern int	mkedebug;

static void
set_debug(const char *optarg)
{
	if (strchr(optarg, 'b')) {
		mkedebug = 1;
	}
	else if (strchr(optarg, 'f'))
		mke_flex_debug = 1;
	else
		mke_flex_debug = 0;
}

#endif

static ECMBOOL
check_mode(char modechr)
{
	switch (modechr) {
	case 'S':
	case 'B':
		if (mode == BINDING_MODE || mode == NONE_MODE) {
			mode = BINDING_MODE;
			return ECM_TRUE;
		}
		break;
	case 'c':
		if (mode == NONE_MODE) {
			mode = COMPILE_MODE;
			return ECM_TRUE;
		}
		if (mode == COMPILE_MODE) {
			fprintf(stderr, "Multiple compilation not supported\n");
			return ECM_FALSE;
		}
		break;
	}
	fprintf(stderr, "mixed mode error\n");
	return ECM_FALSE;
}

static ECMBOOL
add_sobjinfo(const char *sobjname, ECMBOOL standard)
{
	sr_t	*sr_sobjinfo;
	char	*modname;

	if (!ecm_access(sobjname, ECM_R_OK)) {
		fprintf(stderr, "cannot open: %s\n", sobjname);
		return ECM_FALSE;
	}

	modname = check_modname(sobjname, "sobj");
	if (modname == NULL) {
		fprintf(stderr, "Invalid .sobj file(.sobj suffix required)\n");
		return ECM_FALSE;
	}
	if (sr_sobjinfos == NULL)
		sr_sobjinfos = sr_create_dir();
	sobj_num++;
	if (!standard)
		nonstandard_sobj_num++;
	sr_sobjinfo = sr_create_dir();
	sr_append(sr_sobjinfo, sr_create_str(ST_STR, modname));
	sr_append(sr_sobjinfo, sr_create_str(ST_STR, sobjname));
	if (standard) {
		char	*modso, *modso_path;

		ecm_asprintf(&modso, "%s." MODSO_EXT, modname);
		modso_path = ecm_gen_path(mke_clxdir, "dev_modules", modso, NULL);
		ecm_free(modso);
		sr_append(sr_sobjinfo, sr_set_val(sr_create(ST_STR), sr_val_str(modso_path)));
	}
	sr_append(sr_sobjinfos, sr_sobjinfo);
	ecm_free(modname);
	return ECM_TRUE;
}

static ECMBOOL
add_standard_module(const char *modname)
{
	char	*sobj;
	char	*path;
	ECMBOOL	res;

	ecm_asprintf(&sobj, "%s.sobj", modname);
	path = ecm_gen_path(mke_clxdir, "dev_modules", sobj, NULL);
	ecm_free(sobj);

	res = add_sobjinfo(path, ECM_TRUE);
	ecm_free(path);
	return res;
}

static ECMBOOL
parse_args(int argc, char *argv[])
{
	int	c;

#ifdef ECM_DEBUG
	mke_flex_debug = 0;
#endif

	while ((c = ecm_getopt(argc, argv, "S:B:p:c:d:ni:h")) != -1) {
		switch (c) {
		case 'i':
			install_mode = ECM_TRUE;
			inst_clxhome = ecm_strdup(ecm_optarg);
			break;
		case 'S':
		case 'B':
		case 'c':
			if (!check_mode(c))
				return ECM_FALSE;
			if (c == 'S') {
				if (!add_standard_module(ecm_optarg))
					return ECM_FALSE;
			}
			else if (c == 'B') {
				if (!add_sobjinfo(ecm_optarg, ECM_FALSE))
					return ECM_FALSE;
			}
			else {
				specsrc = ecm_strdup(ecm_optarg);
				modname = check_modname(ecm_optarg, "spec");
				if (modname == NULL) {
					fprintf(stderr, "Invalid module spec(.spec suffix required)\n");
					return ECM_FALSE;
				}
			}
			break;
		case 'p':
			if (sscanf(ecm_optarg, "%d", &edbd_port) != 1)
				return ECM_FALSE;
			break;
		case 'n':
			dryrun_mode = ECM_TRUE;
			break;
#ifdef ECM_DEBUG
		case 'd':
			set_debug(ecm_optarg);
			break;
#endif
		case 'h':
			usage();
			return ECM_FALSE;
		default:
			usage();
			return ECM_FALSE;
		}
	}

	if (mode == NONE_MODE) {
		usage();
		return ECM_FALSE;
	}
	if (mode == BINDING_MODE) {
		sr_t	*sr_objinfo;
		ecm_uint32	modnum;

		modnum = (ecm_uint32)(argc - ecm_optind);
		if (nonstandard_sobj_num > modnum) {
			fprintf(stderr, "Insufficient ECM module\n");
			return ECM_FALSE;
		}
		else if (nonstandard_sobj_num < modnum) {
			fprintf(stderr, "Too many ECM modules\n");
			return ECM_FALSE;
		}
		SR_FOREACH_CHILD(sr_sobjinfos, NULL, sr_objinfo) {
			if (sr_geti_str(sr_objinfo, 2))
				continue;
			sr_append(sr_objinfo, sr_create_str(ST_STR, argv[ecm_optind++]));
		}
	}
	return ECM_TRUE;
}

static ECMBOOL
process_compile(const char *modname, const char *specsrc)
{
	sr_t	*sr;

	fp_input = ecm_fopen(specsrc, "r");
	if (fp_input == NULL) {
		fprintf(stderr, "%s: Cannot open\n", specsrc);
		return ECM_FALSE;
	}

	if (mkeparse(&sr) != 0) {
		fprintf(stderr, "%s: Syntax error\n", specsrc);
		return ECM_FALSE;
	}

	if (!make_spec_obj(modname, sr)) {
		fprintf(stderr, "%s: Compilation failed!\n", specsrc);
		return ECM_FALSE;
	}
	fprintf(stderr, "%s: Successfully compiled\n", specsrc);
	return ECM_TRUE;
}

static void
setup_env(void)
{
	const char	*env;
	
	if ((env = ecm_getenv(ECM_CLXDEV_HOME_ENV))) {
		mke_clxdir = ecm_strdup(env);
	}
	else
		mke_clxdir = ecm_strdup(ecm_default_clxdir());
}

int
main(int argc, char *argv[])
{
	ecm_initialize();

	setup_env();

	if (!parse_args(argc, argv))
		return 1;

	if (!eds_initialize()) {
		fprintf(stderr, "can't initialize type\n");
		return 2;
	}

	if (mode == COMPILE_MODE) {
		if (!process_compile(modname, specsrc))
			return 1;
	} else if (mode == BINDING_MODE) {
		if (process_binding(sr_sobjinfos) == ECM_TRUE) {
			fprintf(stdout, "BINDING COMPLETED !!\n");
		} else {
			fprintf(stderr, "BIND ERROR:: FAILED TO BIND!!\n");
			return 1;
		}
	}
	return 0;
}
